package com.ghck.server.service.impl;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.env.Environment;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ghck.message.service.SmsSendService;
import com.ghck.server.controller.events.CreditsEvent;
import com.ghck.server.dao.AjkAccountMapper;
import com.ghck.server.dao.LoginLogMapper;
import com.ghck.server.dao.UsersMapper;
import com.ghck.server.exception.GhckException;
import com.ghck.server.exception.ErrorConstant;
import com.ghck.server.model.AjkAccount;
import com.ghck.server.model.CreditsCategory;
import com.ghck.server.model.EaseUser;
import com.ghck.server.model.LoginLog;
import com.ghck.server.model.Users;
import com.ghck.server.model.query.AjkAccountQuery;
import com.ghck.server.model.request.ForgotPassword;
import com.ghck.server.model.request.GenerateVerCode;
import com.ghck.server.model.request.Login;
import com.ghck.server.model.request.Registration;
import com.ghck.server.model.request.ResetPassword;
import com.ghck.server.model.response.RegionDictRes;
import com.ghck.server.service.AjkAccountService;
import com.ghck.server.service.EasemobMessageService;
import com.ghck.server.service.RegionDictService;
import com.ghck.server.service.SmsVerificationService;
import com.ghck.server.service.UsersService;
import com.ghck.server.utils.CryptoUtil;
import com.ghck.server.utils.IntegerUtil;
import com.ghck.server.utils.MD5Tools;
import com.ghck.server.utils.SpringContextUtil;

/**
 * 账号接口认证表  <br>
 * ajk_account <br>
 * v1.0 2016-05-01
 */
@Service
@Transactional
public class AjkAccountServiceImpl implements AjkAccountService {

	private static final Logger logger = LoggerFactory.getLogger(AjkAccountServiceImpl.class); 

	@Autowired
	private AjkAccountMapper ajkAccountMapper;

	@Autowired
	@Qualifier("smsSendService")
	SmsSendService smsSendService;

	@Autowired
	SmsVerificationService smsVerificationService;

	@Autowired
	UsersService usersService;
	
	@Autowired
	LoginLogMapper loginLogMapper;

	@Autowired
	Environment evn;
	
	@Autowired
	@Qualifier("userDetailsService")
	UserDetailsService userDetailsService;
	
	@Autowired
	@Qualifier("userCache")
	UserCache userCache;
	
	@Autowired
	UsersMapper usersMapper;
	
	@Autowired
	EasemobMessageService easemobMessageService;
	
	@Autowired
	RegionDictService regionDictService;
	
	/**
	 *根据ID删除 账号接口认证表
	 */
	@Override
	public Integer deleteAjkAccountById(Integer id){
		return ajkAccountMapper.deleteById(id);
	}

	/**
	 *新增 账号接口认证表
	 */
	@Override
	@Transactional
	public Integer batchInsertAjkAccount(List<? extends AjkAccount> ajkAccounts){
		Integer count = ajkAccountMapper.batchInsert(ajkAccounts);
		return count;
	}
	/**
	 *新增 账号接口认证表
	 */
	@Override
	@Transactional
	public Integer insertAjkAccount(AjkAccount domain){
		Integer id = ajkAccountMapper.insert(domain);
		return id;
	}

	/**
	 *更新 账号接口认证表
	 */
	@Override
	@Transactional
	public Integer updateAjkAccount(AjkAccount domain){
		Integer count = ajkAccountMapper.update(domain);
		return count;
	}

	/**
	 *根据ID查询 账号接口认证表
	 */
	public AjkAccount findAjkAccountById(Integer id){
		AjkAccount result = ajkAccountMapper.findById(id);
		if(result!=null){
			result.dealWith();
		}
		return result;
	}

	/**
	 *通过query 查询 账号接口认证表
	 */
	public List<AjkAccount> findAjkAccountByQuery(AjkAccountQuery query){
		query.dealWith();
		List<AjkAccount> result = ajkAccountMapper.findByQuery(query);
		if(CollectionUtils.isNotEmpty(result)) {
			for (AjkAccount ajkAccount : result) {
				ajkAccount.dealWith();
			}
		}
		return result;
	}	

	/**
	 *通过query count总条数
	 */
	public Integer countAjkAccountByQuery(AjkAccountQuery query){
		query.dealWith();
		Integer result = ajkAccountMapper.countByQuery(query);
		return result;
	}

	/**
	 * 通过ids查询
	 */
	public List<AjkAccount> findAjkAccountByIds(List<Integer> ids){
		List<AjkAccount> result = ajkAccountMapper.findByIds(ids);
		if(CollectionUtils.isNotEmpty(result)) {
			for (AjkAccount ajkAccount : result) {
				ajkAccount.dealWith();
			}
		}
		return result;
	}
	
	@Override
	public void flushCacheByAccessId(String accessId) {
		UserDetails userDetails = userDetailsService.loadUserByUsername(accessId);
		userCache.putUserInCache(userDetails);
	}

	public AjkAccount findByAccessId(String accessId){
		return ajkAccountMapper.findByAccessId(accessId);
	}



	@Override
	public Integer generateVerificationCode(GenerateVerCode generateVerCode) {
		return smsVerificationService.generateVerificationCode(generateVerCode);
	}
	
	private Date getAccountExpiredTime(){
		int account_expired_time = Integer.parseInt(evn.getProperty("config.account_expired_time"));
		DateTime accountExpiredTime = DateTime.now();
		accountExpiredTime = accountExpiredTime.plusDays(account_expired_time);
		return accountExpiredTime.toDate();
	}
	public AjkAccount createAndInsertAccount(Integer userId,Integer role){
		Date accountExpiredTime = getAccountExpiredTime();
		
		AjkAccount accountDB = ajkAccountMapper.findByUserId(userId);
		if(accountDB!=null){
			accountDB.setAccountExpiredTime(accountExpiredTime);
			return accountDB;
		}
		String accessId = null;
		String accessKey = null;
		try {
			accessId = CryptoUtil.generateUniqueAccessId();
			accessKey = CryptoUtil.generateUniqueAccessKey();
		} catch (Exception e1) {
			GhckException exception = new GhckException(ErrorConstant.OPERATION_ERROR.getErrorCode(),e1.getMessage());
			logger.error("创建accessId,accessKey失败",exception);
			throw exception;
		}
		AjkAccount account = new AjkAccount();
		account.setUserId(userId);
		account.setAccessId(accessId);
		account.setAccessKey(accessKey);
		account.setAccountExpiredTime(accountExpiredTime);
		account.setIsBlock(0);
		account.setRole(role);
		ajkAccountMapper.insert(account);
		return account;
	}

	/**
	 * 只提供患者和管理员注册
	 */
	@Override
	@Transactional
	public Map<String, Object> register(Registration registration) {
		String name = registration.getName();
		String mobile = registration.getMobile();
		String verificationCode = registration.getVerificationCode();
		String originPwd = registration.getPassword();
		Integer role = registration.getRole();

		smsVerificationService.verificationSmsCode(mobile, verificationCode);

		if(usersService.findUsersByMobile(mobile)!=null)
		{
			GhckException exception = new GhckException(ErrorConstant.OPERATION_ERROR.getErrorCode(),"手机号已被注册，请换一个");
			logger.info("注册用户失败",exception);
			throw exception;
		}
		String password = MD5Tools.MD5(originPwd);
		Users users = new Users();
		users.setName(name);
		users.setMobile(mobile);
		users.setPassword(password);
		
		if(IntegerUtil.isPositiveInteger(registration.getProvinceId())){
			users.setProvinceId(registration.getProvinceId());
			RegionDictRes regionDictRes = regionDictService.findRegionDictById(users.getProvinceId());
			users.setProvinceName(regionDictRes.getName());
		}
		if(IntegerUtil.isPositiveInteger(registration.getCityId())){
			users.setCityId(registration.getCityId());
			RegionDictRes regionDictRes = regionDictService.findRegionDictById(users.getCityId());
			users.setCityName(regionDictRes.getName());
		}
		if(IntegerUtil.isPositiveInteger(registration.getRegionId())){
			users.setRegionId(registration.getRegionId());
			RegionDictRes regionDictRes = regionDictService.findRegionDictById(users.getRegionId());
			users.setRegionName(regionDictRes.getName());
		}
		users.setRole(role);
		usersService.insertUsers(users);
		
		Integer userId = users.getId();
		
		AjkAccount ajkAccount = createAndInsertAccount(userId, role);
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("accessId", ajkAccount.getAccessId());
		map.put("accessKey", ajkAccount.getAccessKey());
		map.put("userId",userId);
		
		//注册环信账号
		EaseUser user = easemobMessageService.findByEaseUserByUserId(userId);
		
		//首次登录
		map.put("isFirstLogin", true);
		map.put("easeUser", user);
		
		//注册送积分
		//CreditsEvent event = new CreditsEvent(userId, userId, CreditsCategory.REGISTER);
		//SpringContextUtil.getApplicationContext().publishEvent(event);
		
		return map;
	}

	@Override
	public Map<String, Object> login(Login login) {
		String mobile = login.getMobile();
		String originPwd = login.getPassword();
		Users loginUser = usersService.findUsersByMobile(mobile);
		if(loginUser==null){
			GhckException exception = new GhckException(ErrorConstant.OPERATION_ERROR.getErrorCode(),"账号不存在");
			logger.info("登录失败",exception);
			throw exception;
		}
		String password = MD5Tools.MD5(originPwd);
		if(!StringUtils.equals(password, loginUser.getPassword())){
			GhckException exception = new GhckException(ErrorConstant.OPERATION_ERROR.getErrorCode(),"密码错误");
			logger.info("登录失败",exception);
			throw exception;
		}
		Integer userId = loginUser.getId();
		Integer role = loginUser.getRole();
		AjkAccount ajkAccount = createAndInsertAccount(userId, role);
		if(ajkAccount==null){
			GhckException exception = new GhckException(ErrorConstant.OPERATION_ERROR.getErrorCode(),"未知错误，创建account失败");
			logger.info("登录失败",exception);
			throw exception;
		}
		
		ajkAccountMapper.update(ajkAccount);
		
		String accessId = ajkAccount.getAccessId();
		String accessKey = ajkAccount.getAccessKey();
		//刷新userCache
		flushCacheByAccessId(accessId);
		
		//记录登录日志
		LoginLog log = new LoginLog();
		log.setUserId(userId);
		loginLogMapper.insert(log);
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("accessId", accessId);
		map.put("accessKey", accessKey);
		map.put("userId", userId);
		//首次登录
		map.put("isFirstLogin", false);
		
		EaseUser user = easemobMessageService.findByEaseUserByUserId(userId);
		map.put("easeUser", user);
		return map;
	}

	@Override
	public Integer forgotPassword(ForgotPassword forgotPassword) {
		String mobile = forgotPassword.getMobile();
		String verificationCode = forgotPassword.getVerificationCode();
		String newPassword = forgotPassword.getNewPassword();
		smsVerificationService.verificationSmsCode(mobile, verificationCode);
		Users user = usersService.findUsersByMobile(mobile);
		if(user==null){
			GhckException exception = new GhckException(ErrorConstant.OPERATION_ERROR.getErrorCode(),"账号不存在");
			logger.info("找回密码失败",exception);
			throw exception;
		}
		String password = MD5Tools.MD5(newPassword);
		user.setPassword(password);
		usersMapper.update(user);
		
		Integer userId = user.getId();
		AjkAccount ajkAccount = ajkAccountMapper.findByUserId(userId);
		Date accountExpiredTime = getAccountExpiredTime();
		if(ajkAccount!=null){
			ajkAccount.setAccountExpiredTime(accountExpiredTime);
			ajkAccountMapper.update(ajkAccount);
			flushCacheByAccessId(ajkAccount.getAccessId());
		}
		return 1;
	}
	
	@Override
	public Integer resetPassword(ResetPassword resetPassword) {
		Integer userId = resetPassword.getUserId();
		String newPassword = resetPassword.getNewPassword();
		String oldPassword = resetPassword.getOldPassword();
		
		Users users = usersService.findUsersById(userId);
		if(users==null){
			GhckException exception = new GhckException(ErrorConstant.OPERATION_ERROR.getErrorCode(),"账号不存在");
			logger.info("修改密码失败",exception);
			throw exception;
		}
		String password = MD5Tools.MD5(oldPassword);
		if(!StringUtils.equals(password, users.getPassword())){
			GhckException exception = new GhckException(ErrorConstant.OPERATION_ERROR.getErrorCode(),"旧密码不正确");
			logger.info("修改密码失败",exception);
			throw exception;
		}
		password = MD5Tools.MD5(newPassword);
		users.setPassword(password);
		usersMapper.update(users);
		
		AjkAccount ajkAccount = ajkAccountMapper.findByUserId(userId);
		Date accountExpiredTime = getAccountExpiredTime();
		if(ajkAccount!=null){
			ajkAccount.setAccountExpiredTime(accountExpiredTime);
			ajkAccountMapper.update(ajkAccount);
			flushCacheByAccessId(ajkAccount.getAccessId());
		}
		return 1;
	}

}	